<?php

namespace service\express\Util;

use artisan\cache as Redis;
use artisan\db;
use util\DataBaseUtil;

class ValidWaybillPool
{
    /**
     * db instance
     *
     * @var \artisan\db|null
     */
    private static $db = null;

    /**
     * db name
     *
     * @var string
     */
    private static $db_name = 'express';

    /**
     * table name
     *
     * @var string
     */
    private static $table_name = 'express_valid_waybills';

    /**
     * waybill_source_table name
     *
     * @var string
     */
    private static $waybill_source_table = 'proxy_query_log';

    /**
     * update interval default interval is one month = 86400 * 30
     *
     * @var integer
     */
    private $update_interval = 2592000;

    /**
     * cache_key
     *
     * @var string
     */
    private $cache_key_prefix = 'express_valid_waybills:';
    /**
     * waybill_brands
     *
     * @var array
     */
    private $waybill_brands = [
        'lb',
        'yd',
        'dhl',
        'sad',
        'jd',
        'xf',
        'qy',
        'zt',
        'gt',
        'ups',
        'qf',
        'yt',
        'fedex',
        'sto',
        'lht',
        'yj',
        'dp',
        'se',
        'kj',
        'yc',
        'city100',
        'rfd',
        'zy',
        'ht',
        'wx',
        'qrt',
        'zjs',
        'fedexInter',
        'tt',
        'post',
        'ys',
        'ems',
        'sf',
    ];

    const DEFAULT_CACHE_TIME = 86400;
    /**
     * get some waybills
     *
     * @param  integer $limit
     * @return array
     */
    public function get($limit = 3, $brand = null, $new = false)
    {
        !is_string($brand) and $key = $this->cache_key_prefix.'express_common_waybills'.$limit or $key = $this->cache_key_prefix.md5($limit . $brand);
        if ($new || !($waybills[$key] = Redis::get($this->cache_key_prefix.$key))) {
            if($waybills[$key] = $this->getWaybillByType($brand)){
                Redis::set($this->cache_key_prefix.$key, serialize($waybills[$key]), static::DEFAULT_CACHE_TIME);
            }
        }
        return is_array($waybills[$key]) ? $waybills[$key] : $waybills[$key] = unserialize($waybills[$key]);
    }

    /**
     * updateWaybillPool
     *
     * @param  array|string $brand
     * @return boolean
     */
    public function updateWaybillPool($brands = [])
    {
        if (is_string($brands) && ($brands = trim($brands))) {
            $brands = [$brands];
        }elseif(!$brands){
            $brands = [];
        }
        if (is_array($brands)) {
            $brands = array_unique(array_merge((array) $this->waybill_brands, $brands));
        }
        $data = [];
        foreach ((array)$brands as $brand) {
            $data += $this->getNewWaybills($brand, 10);
        }
        $this->clearOldInfos();
        return $this->insertToDB($data);
    }

    /**
     * setUpdateInterval
     *
     * @param interval $interval
     * @return null|integer
     */
    public function setUpdateInterval($interval = null)
    {
        return is_numeric($interval) ? $this->update_interval = (int) $interval : null;
    }

    /**
     * getNewWaybills
     *
     * @param  string $brand
     * @param  integer $limit
     * @return array
     */
    private function getNewWaybills($brand = null, $limit = 50)
    {
        $brand === null and count($this->waybill_brands) and $brand = $this->waybill_brands[array_rand($this->waybill_brands)];
        // $condition = [
        //     'express_company' => $brand,
        //     'query_status' => 'success',
        //     // 'query_source' => $source,
        // ];
        // if ($data = db::connect(static::$db_name)->table(static::$waybill_source_table)->select('express_company as brand,query_source as source, express_no as waybill_no')->getAll($condition, $limit, 'query_time DESC')) {

        $sql = 'SELECT DISTINCT(express_no) as waybill_no, express_company as brand, query_source as source FROM '.static::$db_name.'.'.static::$waybill_source_table.' WHERE query_status = "success" AND express_company = "'.$brand.'"  ORDER BY query_time DESC LIMIT '.(is_int($limit)?$limit:50);
        if ($data = db::connect(static::$db_name)->query($sql)) {
            return is_array($data) ? $data : [];
        }
        return [];
    }

    /**
     * getWaybillByType
     *
     * @param  string|null  $brand
     * @param  integer $limit
     * @return array
     */
    private function getWaybillByType($brand = null, $limit = 100)
    {
        if ($brand === null) {
            return $this->getWaybillBySource(null, $limit);
        }
        if (is_string($brand)) {
            $condition['brand'] = $brand;
        }
        if ($data = static::getDB()->select('waybill_no, source, brand')->getAll($condition, $limit)) {
            return $data;
        } else {
            return $this->updateWaybillPool($brand) ? $this->getWaybillByType($brand, $limit) : [];
        }
    }

    /**
     * getWaybillBySource
     *
     * @param  string  $source
     * @param  integer $limit
     * @return array
     */
    private function getWaybillBySource($source = null, $limit = 100)
    {
        $condition = [];
        if (is_string($source)) {
            $condition['source'] = $source;
        }
        if ($data = static::getDB()->select('waybill_no, source, brand')->getAll($condition, $limit)) {
            return $data;
        } else {
            return $this->updateWaybillPool() ? $this->getWaybillBySource($source, $limit) : [];
        }
    }

    /**
     * insertToDB
     *
     * @param  array  $data
     * @return mixed
     */
    private function insertToDB(array $data = [])
    {
        if($sql = DataBaseUtil::arrayToInsertSql($data, static::$db_name.'.'.static::$table_name, 'INSERT IGNORE')){
            if ($result = static::getDB()->query($sql)) {
                Redis::set($this->cache_key_prefix.'last_update', time(), $this->update_interval);
            }
            return $result;
        }
    }


    /**
     * clearOldInfos
     *
     * @param  string|null $time
     * @return mixed integer|boolean
     */
    private function clearOldInfos($time = null)
    {
        if (!preg_match('/^\d{4}(?:-\d{2}){2}\s\d{2}(?:\:\d{2}){2}$/', $time)) {
            $time = date('Y-m-d H:i:s', strtotime('-1 month'));
        }
        $condition = [
            'create_at <' => $time,
        ];
        return static::getDB()->delete($condition);
    }

    /**
     * getDB
     *
     * @param  string $table
     * @return \artisan\db;
     */
    private static function getDB($table = '')
    {
        if (static::$db === null) {
            static::$db = db::connect(static::$db_name);
        }
        return static::$db->table($table ?: static::$table_name);
    }

    /**
     * __destruct
     */
    public function __destruct()
    {
        //cron or on this time
        !Redis::get($this->cache_key_prefix.'last_update') and $this->updateWaybillPool();
    }
}
